// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... namespace LargoCommon.Music { using Abstract; using JetBrains.Annotations; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Xml.Linq; /// /// Harmonic Motive. /// [Serializable] public sealed class HarmonicBar { #region Fields /// /// Harmonic Structures. /// [NonSerialized] private IList harmonicStructures; /// /// Rhythmic Structure. /// private RhythmicStructure rhythmicStructure; /// /// Harmonic Modality. /// private HarmonicModality harmonicModality; /// /// Simple structural Outline. /// private string simpleStructuralOutline; /// /// Structural Outline. /// private string structuralOutline; /// /// The bar metric code. /// private string barMetricCode; /// /// The harmonic modality code. /// private string harmonicModalityCode; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The given bar number. /// The original bar number. public HarmonicBar(int givenBarNumber, int originalBarNumber) { this.RhythmicBehavior = new RhythmicBehavior(); this.HarmonicBehavior = new HarmonicBehavior(); this.harmonicStructures = new List(); this.BarNumber = givenBarNumber; this.OriginalBarNumber = originalBarNumber; } /// /// Initializes a new instance of the class. /// /// The given header. /// The given bar. public HarmonicBar(MusicalHeader givenHeader, HarmonicBar givenBar) { this.Header = givenHeader; //// Convert given bar under given (style) header... this.RhythmicBehavior = givenBar.RhythmicBehavior; this.HarmonicBehavior = givenBar.HarmonicBehavior; this.harmonicStructures = new List(); this.BarNumber = givenBar.BarNumber; this.OriginalBarNumber = givenBar.OriginalBarNumber; this.HarmonicModality = givenBar.HarmonicModality; this.RhythmicStructure = givenBar.RhythmicStructure.ConvertToSystem(givenHeader.System.RhythmicSystem); this.RhythmicShape = this.RhythmicStructure.GetRhythmicShape; int idx = 0; var distances = this.RhythmicShape.BitDistances; foreach (var givenHarStruct in givenBar.harmonicStructures) { var harStruct = (HarmonicStructure)givenHarStruct.Clone(); if (idx < distances.Count) { harStruct.Length = distances[idx++]; } this.harmonicStructures.Add(harStruct); } //// var rsystem = this.RhythmicSystemFromStructures; this.RhythmicShape = this.RhythmicShapeFromStructures(givenHeader.System.RhythmicSystem); this.barMetricCode = this.BarMetricFromStructures(givenHeader.System.RhythmicSystem); } /// /// Initializes a new instance of the class. /// /// The given header. /// The mark bar. public HarmonicBar(MusicalHeader givenHeader, XElement markHarmony) { int barNumber = XmlSupport.ReadIntegerAttribute(markHarmony.Attribute("Number")); int originalBarNumber = XmlSupport.ReadIntegerAttribute(markHarmony.Attribute("OriginalNumber")); this.RhythmicBehavior = new RhythmicBehavior(); this.HarmonicBehavior = new HarmonicBehavior(); this.harmonicStructures = new List(); this.BarNumber = barNumber; this.OriginalBarNumber = originalBarNumber; this.Header = givenHeader; var xstructure = markHarmony.Elements("Structure"); if (xstructure != null) { //// var xstructure = xElement.Elements("Structure"); StringBuilder sb = new StringBuilder(); foreach (var xst in xstructure) { var code = (string)xst.Attribute("Code"); var start = (int)xst.Attribute("Start"); var length = (int)xst.Attribute("Length"); var st = new HarmonicStructure(this.Header.System.HarmonicSystem, code) { BitFrom = (byte)start, Length = (byte)length }; var shortcut = XmlSupport.ReadStringAttribute(xst.Attribute("Shortcut")); st.DetermineBehavior(); st.Shortcut = shortcut; //// st.Base = chordBase; this.AddStructure(st); sb.Append(shortcut); sb.Append(","); } //// bar.RhythmicStructure; this.StructuralOutline = sb.ToString(); var rhyStruct = this.RhythmicStructure; this.RhythmicShape = new RhythmicShape(rhyStruct.RhythmicSystem.Order, rhyStruct); this.SetBarMetricCode(this.RhythmicShape.GetStructuralCode); } } /// /// Initializes a new instance of the class. /// /// The given header. /// The harmonic structure. public HarmonicBar(MusicalHeader givenHeader, HarmonicStructure harmonicStructure) { this.Header = givenHeader; this.SetStructure(harmonicStructure); } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public XElement GetXElement { get { var xelement = new XElement("Harmony"); xelement.Add(new XAttribute("Length", this.Length)); foreach (var harStruct in this.HarmonicStructures) { var xstruct = harStruct.GetXElement; xelement.Add(xstruct); } return xelement; } } #endregion #region Main Properties /// /// Gets or sets the musical header. /// /// /// The musical header. /// public MusicalHeader Header { get; set; } /// /// Gets or sets the Bar Number In Motive. /// /// /// Property description. /// public int BarNumber { get; set; } /// /// Gets or sets the bar number. /// /// /// The bar number. /// public int OriginalBarNumber { get; set; } /// /// Gets or sets the harmonic structures. /// /// /// The harmonic structures. /// public IList HarmonicStructures { get { Contract.Ensures(Contract.Result>() != null); if (this.harmonicStructures == null) { throw new InvalidOperationException("Harmonic structures are null."); } return this.harmonicStructures; } set => this.harmonicStructures = value ?? throw new ArgumentException("Argument cannot be empty.", nameof(value)); } /// Gets a value indicating whether if bar is empty. /// Property description. /// Returns value. public bool IsEmpty => this.HarmonicStructures.Count == 0; /// Gets unique identifier. /// Property description. public string UniqueIdentifier { get { var ident = new StringBuilder(); foreach (var b in from ms in this.HarmonicStructures where ms != null from b in ms.GetStructuralCode select b) { ident.Append(b); //// ElementSchema, DecimalNumber, ms.StructuralCode } return ident.ToString(); } } /// /// Gets or sets the Harmonic Modality. /// /// /// Property description. /// public HarmonicModality HarmonicModality { get { if (this.harmonicModality != null) { return this.harmonicModality; } var modalityCode = this.GetHarmonicModalityCode; if (string.IsNullOrEmpty(modalityCode)) { return this.harmonicModality; } const byte order = DefaultValue.HarmonicOrder; //// this.harMotiveBar.HarmonicMotive.THarmonicMotive.THarmonicCore.HarmonicOrder; var hs = HarmonicSystem.GetHarmonicSystem(order); this.harmonicModality = HarmonicModality.GetNewHarmonicModality(hs, modalityCode); return this.harmonicModality; } set => this.harmonicModality = value; } /// /// Gets or sets the harmonic motive. /// /// /// The harmonic motive. /// public HarmonicMotive HarmonicMotive { get; set; } #endregion #region Derived Properties /// /// Gets or sets the simple structural outline. /// /// /// Property description. /// public string SimpleStructuralOutline { get { if (!string.IsNullOrEmpty(this.simpleStructuralOutline)) { return this.simpleStructuralOutline; } var sb = new StringBuilder(); //// HarmonicModality modality = this.HarmonicModality; foreach (var harStruct in this.HarmonicStructures) { if (sb.Length > 0) { sb.Append(" "); } sb.Append(harStruct.Shortcut); } this.simpleStructuralOutline = sb.ToString(); return this.simpleStructuralOutline; } set => this.simpleStructuralOutline = value; } /// /// Gets the outline. /// /// /// The outline. /// [UsedImplicitly] public string Outline => $"{this.BarNumber}/{this.StructuralOutline}"; /// /// Gets or sets HarmonicStructureOutline. /// /// General musical property. public string StructuralOutline { get { if (this.structuralOutline != null) { return this.structuralOutline; } var sb = new StringBuilder(); //// HarmonicModality modality = this.HarmonicModality; foreach (var harStruct in this.HarmonicStructures) { if (sb.Length > 0) { sb.Append(" "); } sb.Append(harStruct.Shortcut); sb.Append(string.Format(CultureInfo.InvariantCulture, "({0})", harStruct.Length.ToString(CultureInfo.InvariantCulture))); } this.structuralOutline = sb.ToString(); return this.structuralOutline; } set => this.structuralOutline = value; } /// /// Gets the length. /// /// Property description. public int Length => this.HarmonicStructures.Count; /// /// Gets the bar metric code. /// /// Returns value. public string GetBarMetricCode => this.barMetricCode?.Trim(); /// /// Gets the harmonic modality code. /// /// Returns value. public string GetHarmonicModalityCode => this.harmonicModalityCode?.Trim(); #endregion #region Physical properties - Public /// /// Gets or sets the harmonic behavior. /// /// /// The harmonic behavior. /// public HarmonicBehavior HarmonicBehavior { get; set; } /// /// Gets or sets the rhythmic behavior. /// /// /// The rhythmic behavior. /// public RhythmicBehavior RhythmicBehavior { get; set; } /// /// Gets or sets the rhythmic structure. /// /// /// General musical property. /// public RhythmicStructure RhythmicStructure { get { //// Contract.Requires(this.RhythmicSystemFromStructures != null); if (this.rhythmicStructure != null) { return this.rhythmicStructure; } byte rorder = 0; //// ?!?!? if (this.Header != null) { rorder = this.Header.System.RhythmicOrder; } var musicalHeader = this.Header; if (musicalHeader != null) { var system = musicalHeader.System; //// if (HarmonicMotive?.Core != null) { rorder = system.RhythmicOrder; //// } } var rsystem = rorder > 0 ? RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rorder) : this.RhythmicSystemFromStructures; if (rsystem == null) { return null; //// throw new InvalidOperationException("Unknown Rhythmic System."); } this.RhythmicShape = this.RhythmicShapeFromStructures(rsystem); var rs = new RhythmicStructure(rsystem.Order, this.RhythmicShape); return rs; } set => this.rhythmicStructure = value; } #endregion #region Properties - Rhythmic Status /// Gets or sets rhythmical shape. /// Property description. public RhythmicShape RhythmicShape { get; set; } #endregion #region Public properties - String representation /// Gets Write particular structures to string. /// Returns value. /// General musical property. public string PureChordsToString { get { var s = new StringBuilder(); //// s.Append("Chords:"); //// +"\n" byte lev = 0; foreach (var hs in this.HarmonicStructures.Where(hs => (hs != null && this.RhythmicShape != null) && this.RhythmicShape.Number > 0)) { var duration = this.RhythmicShape.DistanceAtLevel(lev++); s.AppendFormat(CultureInfo.CurrentCulture, "{0,15} ", hs.ToneSchema); s.AppendFormat(CultureInfo.CurrentCulture, "({0,2}), ", duration.ToString("D", CultureInfo.CurrentCulture.NumberFormat)); } return s.ToString().Trim(); } } /// /// Gets modality as string. /// /// General musical property. /// Returns value. public string ModalityToString { get { var s = new StringBuilder(); if (this.HarmonicStructures.Count <= 0) { return s.ToString(); } var harStruct = this.HarmonicStructures[0]; if (harStruct?.HarmonicModality != null) { s.AppendFormat(CultureInfo.CurrentCulture, "{0,20} ", harStruct.HarmonicModality.ToneSchema); } return s.ToString(); } } /// Gets particular structures to string. /// Property description. /// Returns value. [UsedImplicitly] public string ModalitiesToString { get { var s = new StringBuilder(); //// byte lev = 0; //// byte duration; long lastNumber = 0; // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var hs in this.HarmonicStructures .Where(hs => hs?.HarmonicModality != null)) { if (hs.HarmonicModality.Number == lastNumber) { continue; } //// duration = RhythmicShape.DistanceAtLevel(lev++); s.Append(hs.HarmonicModality.ToneSchema + "\r"); lastNumber = hs.HarmonicModality.Number; } return s.ToString(); } } /// /// Gets to string. /// /// General musical property. public string ChordsToString { get { var s = new StringBuilder(); //// s.Append("Chords:"); //// +"\n" byte level = 0; foreach (var hs in this.HarmonicStructures.Where(hs => hs != null && this.RhythmicShape != null)) { var duration = this.RhythmicShape.DistanceAtLevel(level++); s.AppendFormat(CultureInfo.CurrentCulture, "{0,15}", hs.HarmonicModality != null ? hs.HarmonicModality.ToneSchema : string.Empty); s.AppendFormat(CultureInfo.CurrentCulture, "{0,15}", hs.ToneSchema); s.AppendFormat(CultureInfo.CurrentCulture, "({0,2}),", duration.ToString("D", CultureInfo.CurrentCulture.NumberFormat)); } return s.ToString().Trim(); } } #endregion #region Public properties /// Gets inner measure of dissonance. /// Property description. public float MeanConsonance { get { if (this.Length == 0) { return 0; } var total = (from HarmonicStructure s in this.HarmonicStructures select s.HarmonicBehavior.Consonance).Sum(); var meanConsonance = total / this.Length; return meanConsonance; } } /// Gets inner continuity. /// Property description. public float MeanPotential { get { if (this.Length == 0) { return 0; } float total = 0; var cnt = 0; foreach (var s in this.HarmonicStructures) { if (s != null) { total += s.HarmonicBehavior.Potential; cnt++; } } var result = cnt != 0 ? total / cnt : 0; return result; } } /// Gets inner continuity. /// Property description. public float MeanContinuity { get { if (this.Length == 0) { return 0; } HarmonicStructure p = null; float total = 0; var cnt = 0; foreach (var s in this.HarmonicStructures) { if (p != null) { var r = new HarmonicRelation(s.HarmonicSystem, p, s); total += r.FormalContinuity; cnt++; } p = s; } var result = cnt != 0 ? total / cnt : 100; return result; } } /// Gets inner impulse. /// Property description. public float MeanImpulse { get { if (this.Length == 0) { return 0; } HarmonicStructure p = null; float total = 0; var cnt = 0; foreach (var s in this.HarmonicStructures) { if (p != null) { var r = new HarmonicRelation(s.HarmonicSystem, p, s); total += r.FormalImpulse; cnt++; } p = s; } var result = cnt != 0 ? total / cnt : 0; return result; } } /// /// Gets the mean rhythmic mobility. /// public float MeanRhythmicMobility { get { //// Contract.Requires(this.RhythmicSystemFromStructures != null); var rs = this.RhythmicStructure; if (!rs.HasProperties) { rs.DetermineBehavior(); } return rs.RhythmicBehavior.Mobility; } } /// /// Gets the mean rhythmic tension. /// public float MeanRhythmicTension { get { Contract.Requires(this.RhythmicSystemFromStructures != null); var rs = this.RhythmicStructure; if (!rs.HasProperties) { rs.DetermineBehavior(); } return rs.FormalBehavior.Variance; } } /// /// Gets the rhythmic system from structures. /// private RhythmicSystem RhythmicSystemFromStructures { get { var structures = this.HarmonicStructures; var rhythmicOrder = structures.Aggregate(0, (current, structure) => (byte)(current + structure.Length)); if (rhythmicOrder == 0) { return null; } var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Shape, rhythmicOrder); return rsystem; } } #endregion #region Public static methods /// /// Empties the bar. /// /// The harmonic order. /// The rhythmic order. /// /// Returns value. /// public static HarmonicBar EmptyBar(byte harmonicOrder, byte rhythmicOrder) { var harmonicBar = new HarmonicBar(0, 0); var structuralCode = string.Empty; //// new byte[0]; var system = HarmonicSystem.GetHarmonicSystem(harmonicOrder); var hs = new HarmonicStructure(system, structuralCode) { BitFrom = 0, Length = rhythmicOrder }; harmonicBar.AddStructure(hs); return harmonicBar; } #endregion #region Public methods /// /// Sets the structure. /// /// The harmonic structure. public void SetStructure(HarmonicStructure harmonicStructure) { this.simpleStructuralOutline = null; this.structuralOutline = null; this.RhythmicBehavior = new RhythmicBehavior(); this.HarmonicBehavior = new HarmonicBehavior(); this.harmonicStructures = new List(); this.AddStructure(harmonicStructure); if (this.RhythmicStructure != null) { var rhyStruct = this.RhythmicStructure; var shape = new RhythmicShape(rhyStruct.RhythmicSystem.Order, rhyStruct); this.SetBarMetricCode(shape.GetStructuralCode); } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { var s = new StringBuilder(); s.AppendFormat(CultureInfo.CurrentCulture, "Bar n.{0,3}: ", this.BarNumber); //\r\n s.Append(this.SimpleStructuralOutline); return s.ToString(); } /// Makes a deep copy of the HarmonicBar object. /// /// Returns object. /// public object Clone() { var bar = new HarmonicBar(0, this.OriginalBarNumber) { Header = this.Header, HarmonicStructures = this.HarmonicStructures }; bar.SetBarMetricCode(this.GetBarMetricCode); bar.SetHarmonicModalityCode(this.GetHarmonicModalityCode); this.RegenerateStructures(); return bar; } /// /// Adds the motive. /// /// The structure. public void AddStructure(HarmonicStructure structure) { ((List)this.HarmonicStructures).Add(structure); } /// /// Determines the bar metric. /// /// The rhythmic system. /// Returns value. public string BarMetricFromStructures(RhythmicSystem rhythmicSystem) { //// rewrite it with help of constructor using BitArray if (rhythmicSystem == null) { return string.Empty; } var barMetric = new BinaryStructure(rhythmicSystem, 0); byte start = 0; foreach (var hs in this.HarmonicStructures) { barMetric.On(start); start += hs.Length; } barMetric.DetermineLevel(); return barMetric.GetStructuralCode; } /// /// Sets the bar metric code. /// /// The given code. public void SetBarMetricCode(string givenCode) { this.barMetricCode = givenCode; } /// /// Sets the harmonic modality code. /// /// The given code. public void SetHarmonicModalityCode(string givenCode) { this.harmonicModalityCode = givenCode; } /// /// Re-computes this instance. /// public void Recompute() { if (this.Length == 0 || this.RhythmicStructure == null) { return; } this.HarmonicBehavior.Consonance = this.MeanConsonance; if (this.HarmonicModality != null) { this.HarmonicBehavior.Potential = this.MeanPotential; } this.HarmonicBehavior.Continuity = this.MeanContinuity; this.HarmonicBehavior.Impulse = this.MeanImpulse; this.RhythmicBehavior.Mobility = this.MeanRhythmicMobility; this.RhythmicBehavior.Tension = this.MeanRhythmicTension; } /// /// Checks the structures. /// public void CheckStructures() { if (!this.HarmonicStructures.Any()) { var harmonicOrder = this.Header.System.HarmonicOrder; //// throw new ArgumentException("Empty har.motive bar structs!"); var harSys = HarmonicSystem.GetHarmonicSystem(harmonicOrder); var harStr = new HarmonicStructure(harSys, 0L); //// harStr.DetermineBehavior(); this.AddHarmonicStructure(harStr); } } /// /// Regenerates the structures. /// public void RegenerateStructures() { if (this.Header == null) { return; } var harmonicOrder = this.Header.System.HarmonicOrder; //// var rhythmicOrder = this.Header.System.RhythmicOrder; var harStructures = new List(); //// Microtonal support //// harStructNumber = GeneralSystem.ConvertStruct(harStructNumber, hms.HarmonicOrder, harSys.Order); //// harSys.Degree //// Do not convert to Linq! foreach (var hms in this.HarmonicStructures) { if (hms == null) { continue; } var harSys = HarmonicSystem.GetHarmonicSystem(harmonicOrder); var structuralCode = hms.GetStructuralCode; var harStruct = HarmonicStructure.GetNewHarmonicStructure(harSys, structuralCode); //// harStr.DetermineBehavior(); harStruct.BitFrom = hms.BitFrom; harStruct.Length = hms.Length; harStructures.Add(harStruct); } this.HarmonicStructures = harStructures; } /// Returns harmonic structure at given position. /// Property description. /// Requested level. /// Returns value. public HarmonicStructure HarmonicStructureAtRhythmicLevel(byte givenLevel) { if (this.HarmonicStructures.Count == 0 || givenLevel >= this.HarmonicStructures.Count) { return null; } return this.HarmonicStructures[givenLevel]; } /// Returns harmonic structure at given bit. /// Requested tick. /// Returns value. public HarmonicStructure HarmonicStructureAtTick(byte givenTick) { if (this.RhythmicShape == null) { return null; } var level = this.RhythmicShape.LevelOfBit(givenTick); return this.HarmonicStructureAtRhythmicLevel(level); } /// /// Returns harmonic structure that prevails in given time Range. /// /// Tone Range. /// Single Harmony. /// Returns value. public HarmonicStructure PrevailingHarmonicStructure(BitRange toneRange, out bool simpleHarmony) { simpleHarmony = false; if (this.RhythmicShape == null) { return null; } byte extremeLength = 0, rhythmicLevelAtExtreme = 0; var level = this.RhythmicShape.Level; for (byte i = 0; i < level; i++) { var rhythmicRange = this.RhythmicShape.RangeForLevel(i); var single = rhythmicRange.CoverRange(toneRange); // 2008/12 if (single) { simpleHarmony = true; return this.HarmonicStructureAtRhythmicLevel(i); } var interRange = rhythmicRange.IntersectionWith(toneRange); if (interRange == null || interRange.Length <= extremeLength) { continue; } extremeLength = interRange.Length; rhythmicLevelAtExtreme = i; } //// simpleHarmony = false; return this.HarmonicStructureAtRhythmicLevel(rhythmicLevelAtExtreme); } /// Returns number of bits containing given pitch element. /// Element of pitch. /// Given range. /// Returns value. [UsedImplicitly] public int TotalHarmonicBits(byte givenPitchElement, BitRange range) { if (range == null) { return 0; } byte i, total = 0; byte bitFrom = range.BitFrom, bitTo = range.BitTo; for (i = bitFrom; i <= bitTo; i++) { var harmonicStructure = this.HarmonicStructureAtTick(i); if (harmonicStructure != null) { total += (byte)(harmonicStructure.IsOn(givenPitchElement) ? 1 : 0); } } return total; } #endregion #region Private methods /// /// Rhythmic shape from structures. /// /// The rhythmic system. /// Returns value. private RhythmicShape RhythmicShapeFromStructures(RhythmicSystem rhythmicSystem) { var bitArray = new BitArray(rhythmicSystem.Order); var shape = new RhythmicShape(rhythmicSystem, bitArray); byte start = 0; foreach (var hs in this.HarmonicStructures) { if (start < shape.Order) { //// 2015/01 shape.On(start); } start += hs.Length; } return shape; } /// /// Add Harmonic Struct. /// /// Harmonic structure. private void AddHarmonicStructure(HarmonicStructure harmonicStructure) { if (harmonicStructure != null) { //// && !harmonicStructure.IsEmptyStruct() !? //// // harStr.HarmonicModality = aHarmonicVariety.HarmonicModality; this.HarmonicStructures.Add(harmonicStructure); } } #endregion } }